package repository

import (
	"errors"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEnv"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/FileCompare"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/FileName"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/MD5"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Number"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Slice"
	"strings"
	"sync"
)

type repoCloudFileInstallStatus struct {
	mutex     sync.Mutex
	statusMap map[string]interface{}
}

func (e *repoCloudFileInstallStatus) mapSetValue(keys ...interface{}) {
	e.mutex.Lock()
	defer e.mutex.Unlock()
	Map.SetValue(e.statusMap, keys...)
}

func (e *repoCloudFileInstallStatus) mapGetValue(keys ...interface{}) (interface{}, error) {
	e.mutex.Lock()
	defer e.mutex.Unlock()
	return Map.GetValue(e.statusMap, keys...)
}

func (e *repoCloudFileInstallStatus) ScanRepositoryStatus(modelType string) {
	// 扫描本地目录，初步判定有哪些组件，构造出组件列表
	localList := LocalPathName.findRepoLocalModelType(modelType)

	// 清空原有的数据
	e.mapSetValue(modelType, make(map[string]interface{}))

	// 扫描本地组件的完整状态，扫描出组件的状态
	e.ScanLocalStatus(modelType, localList)

	// 本地存在的组件特征
	localKeys := make([]string, 0)
	for _, data := range localList {
		modelName := Map.GetString(data, "modelName", "")
		version := Map.GetString(data, "version", "")
		stage := Map.GetString(data, "stage", "")
		component := Map.GetString(data, "component", "")

		localKeys = append(localKeys, modelType+":"+modelName+":"+version+":"+stage+":"+component)
	}

	// 获得云端组件列表
	cloudList := CloudCache.ReadList(modelType)

	// 尚未下载云端组件，标识未未下载状态
	for _, data := range cloudList {
		modelName := Map.GetString(data, "modelName", "")
		lastVersion := Map.GetMap(data, "lastVersion", nil)
		versions := Map.GetSlice(data, "versions", make([]interface{}, 0))

		// 合并lastVersion和Versions
		verEntityList := make([]map[string]interface{}, 0)
		if lastVersion != nil {
			verEntityList = append(verEntityList, lastVersion)
		}
		for _, verEntity := range versions {
			verEntityList = append(verEntityList, verEntity.(map[string]interface{}))
		}

		for _, verEntity := range verEntityList {
			version := Map.GetString(verEntity, "version", "")
			stage := Map.GetString(verEntity, "stage", "")
			component := Map.GetString(verEntity, "component", "")

			if Method.HasEmpty(modelName, version, component) {
				continue
			}

			// 检查：该组件是否存在本地
			key := modelType + ":" + modelName + ":" + version + ":" + stage + ":" + component
			if Slice.Has(localKeys, key) {
				continue
			}

			// 标识为未下载状态
			e.mapSetValue(modelType, modelName, version, stage, component, "status", RepoStatusConstant.NotDownloaded)

		}

	}
}

func (e *repoCloudFileInstallStatus) ScanLocalStatus(modelType string, list []map[string]interface{}) {
	for _, data := range list {
		if modelType != data["modelType"] {
			continue
		}

		e.scanLocalStatus(data)
	}
}

func (e *repoCloudFileInstallStatus) scanLocalStatus(comp map[string]interface{}) {
	// 提取业务参数
	modelName := Map.GetString(comp, "modelName", "")
	modelType := Map.GetString(comp, "modelType", "")
	version := Map.GetString(comp, "version", "")
	stage := Map.GetString(comp, "stage", "")
	component := Map.GetString(comp, "component", "")

	// 本地的状态
	e.scanLocalStatusComponent(modelType, modelName, version, stage, component)
	e.scanLocalMd5Component(modelType, modelName, version, stage, component)
}

func (e *repoCloudFileInstallStatus) scanLocalStatusComponent(modelType, modelName, version, stage, component string) error {
	// 简单验证
	if Method.HasEmpty(modelType, modelName, version, stage, component) {
		return errors.New("参数不能为空: modelType, modelName, version, stage, component")
	}

	// 阶段1：未下载
	status := RepoStatusConstant.NotDownloaded

	// 检查：是否已经下载
	packFile := LocalPathName.findRepoLocalRepoTarFile(modelType, modelName, version, stage, component)
	if packFile == "" {
		e.mapSetValue(modelType, modelName, version, stage, component, "status", status)
		return nil
	}

	// 阶段2："已经下载，待安装!
	status = RepoStatusConstant.Downloaded

	// 检查：解压后的目录是否存在，至少包含一个文件
	tarDir := LocalPathName.getPathName4LocalRepo2tar(modelType, modelName, version, stage, component)
	tarFileNames := FileName.ListFile(tarDir, true, true)
	if len(tarFileNames) == 0 {
		e.mapSetValue(modelType, modelName, version, stage, component, "status", status)
		return nil
	}

	// 阶段3：已经下载，已解压!
	status = RepoStatusConstant.Decompressed

	// 阶段3：已经安装的文件
	for _, name := range tarFileNames {
		tarFileName := name[len(tarDir)+1:]

		srcFileName := ""
		jarFileName := ""

		if "decoder" == modelType {
			srcFileName = tarDir + "/" + tarFileName
			jarFileName = commEnv.Service.WorkDir + "/jar/decoder/" + modelName + ".jar"
		}
		if "template" == modelType {
			srcFileName = tarDir + "/" + tarFileName
			jarFileName = commEnv.Service.WorkDir + "/template/" + modelName + "/" + tarFileName
		}
		if "service" == modelType {
			srcFileName = tarDir + "/" + tarFileName
			jarFileName = commEnv.Service.WorkDir + "/" + tarFileName
		}
		if "webpack" == modelType {
			srcFileName = tarDir + "/" + tarFileName
			jarFileName = commEnv.Service.WorkDir + "/" + tarFileName
		}

		// 检查：目标文件是否存在，如果存在缺失文件，则未安装
		if !FileName.IsFile(jarFileName) {
			// 阶段4："已下载，未安装!"
			status = RepoStatusConstant.NotInstalled
			break
		}

		// 检查：内容是否一致，如果存在不一致，则未安装
		same, err := FileCompare.IsSameFile(jarFileName, srcFileName)
		if err != nil {
			return err
		}
		if !same {
			// 阶段4："已下载，未安装!"
			status = RepoStatusConstant.NotInstalled
			break
		}

		// 阶段5："已下载，已安装!"
		status = RepoStatusConstant.Installed
	}

	e.mapSetValue(modelType, modelName, version, stage, component, "status", status)

	return nil
}

func (e *repoCloudFileInstallStatus) scanLocalMd5Component(modelType, modelName, version, stage, component string) error {
	if Method.HasEmpty(modelName, version, component) {
		return errors.New("参数不能为空: modelName, version, component")
	}

	md5 := ""

	// 检查：是否已经下载
	packFile := LocalPathName.findRepoLocalRepoTarFile(modelType, modelName, version, stage, component)
	if packFile == "" {
		e.mapSetValue(modelType, modelName, version, stage, component, "localMd5", md5)
		return nil
	}

	// 计算MD5
	md5, err := MD5.GetFileMD5Text(packFile)
	if err != nil {
		return err
	}

	e.mapSetValue(modelType, modelName, version, stage, component, "localMd5", md5)
	return nil
}

func (e *repoCloudFileInstallStatus) VerifyMd5Status(modelType, modelName, component string, verEntity map[string]interface{}) int {
	version := Map.GetString(verEntity, "version", "")
	stage := Map.GetString(verEntity, "stage", "")
	if Method.HasEmpty(version, stage) {
		return -1
	}

	status := RepoStatusConstant.NotScanned
	val, _ := e.mapGetValue(modelType, modelName, version, stage, component, "status")
	if !Method.HasNull(val) {
		status = Number.MakeInt(val)
	}

	localMd5 := ""
	val, _ = e.mapGetValue(modelType, modelName, version, stage, component, "localMd5")
	if !Method.HasNull(val) {
		localMd5 = val.(string)
	}

	verEntity["status"] = status
	verEntity["localMd5"] = localMd5

	return e.verifyMd5Status(verEntity, status, localMd5)
}

func (e *repoCloudFileInstallStatus) verifyMd5Status(verEntity map[string]interface{}, status int, localMd5 string) int {
	// 检查：是否已经下载
	if status < RepoStatusConstant.Downloaded {
		return status
	}

	// 在查询阶段，计算破损状态：本地和云端的MD5是否一致
	md5 := Map.GetString(verEntity, "md5", "")
	if strings.ToLower(localMd5) != strings.ToLower(md5) {
		return RepoStatusConstant.DamagedPackage
	}

	return status
}

func (e *repoCloudFileInstallStatus) verifyUpgradeStatus(modelType, modelName string, lastVersion map[string]interface{}, versions []interface{}) bool {
	if len(lastVersion) == 0 {
		return false
	}

	lastVer := Map.GetString(lastVersion, "version", "")
	lastStage := Map.GetString(lastVersion, "stage", "")
	lastComponent := Map.GetString(lastVersion, "component", "")

	// 如果最新版本就是安装版本，那么不需要升级
	lastStatus := RepoStatusConstant.NotScanned
	val, _ := e.mapGetValue(modelType, modelName, lastVer, lastStage, lastComponent, "status")
	if val == nil {
		lastStatus = Number.MakeInt(val)
	}
	if RepoStatusConstant.Installed == lastStatus {
		return false
	}

	// 明细版本
	for _, v := range versions {
		verEntity := v.(map[string]interface{})

		version := Map.GetString(verEntity, "version", "")
		stage := Map.GetString(verEntity, "stage :=", "")
		component := Map.GetString(verEntity, "component", "")

		// 排除last版本
		if version == lastVer && stage == lastStage && component == lastComponent {
			continue
		}

		// 检查：低版本是否处于安装状态，如果是安装状态，那么就是需要用最新版本来升级
		status := RepoStatusConstant.NotScanned
		val, _ := e.mapGetValue(modelType, modelName, version, stage, component, "status")
		if val == nil {
			status = Number.MakeInt(val)
		}
		if RepoStatusConstant.Installed == status {
			return true
		}
	}

	return false
}
